# -*- coding: utf-8 -*-
###
# @Author: CPS
# @Date: 2020-07-24 16:10:47
# @Last Modified by: CPS
# @Last Modified time: 2020-09-27 23:44:44
# @file_path Z:\CPS\IDE\SublimeText\3176x32前端\Data\Packages\testt\Testt.py
# @Filename Testt.py
###

import sublime
import sublime_plugin
import os,sys,json
import platform
import datetime
from subprocess import Popen, PIPE

# 默认配置目录
TESTT_PATH = ''

# 是否开启 debug
DEBUG_FALG = False

lib = False

# 载入默认配置文件
# TESTT_SETTINGS = False
# STYLUS_OPTIONS = False

# 判断是否存在 nodejs
# HAS_NODE = False

# 拼接 nodejs插件的入口文件 stylus-beautify.js
# NODE_SCRIPT_PATH = ''
# CONFIG_PATH = 'stylusBeautify.sublime-settings'

#当前处理文件的格式
# SYNTAX = ''

# 将当前所有缓冲区存入 tuple,获得可迭代的特性,往后能执行批量处理
sublime.Region.totuple = lambda self: (self.a, self.b)
sublime.Region.__iter__ = lambda self: self.totuple().__iter__()



# 测试函数 [alt + t]
class TesttTest(sublime_plugin.TextCommand):
    def run(self,edit,mode='0'):
        print('当前sytanx: ',self.view.settings().get('syntax'))


#每次插件加载完都会触发这个函数
def plugin_loaded():
    # 获取插件文件夹
    global TESTT_PATH
    TESTT_PATH = os.path.join(sublime.packages_path(),'testt')

    # 引入 testtlib 函数库文件夹到环境变量
    sys.path.append(os.path.join(TESTT_PATH,'testtlib'))

    # 获取插件配置
    global TESTT_SETTINGS
    TESTT_SETTINGS = sublime.load_settings('testt.sublime-settings')

    # 引入 updatebuild 函数库文件夹到环境变量
    # 根据 shell 目录, 自动生成 sublime 的编译关联
    sys.path.append(os.path.join(TESTT_PATH,'updatebuild'))
    import testtBuild
    testtBuild.updateBuildFile(os.path.join(sublime.packages_path(),'testt','Shell'))

    # 检查系统是否有node
    global HAS_NODE
    import testtlib
    HAS_NODE = testtlib.check_node()
    if not HAS_NODE:
        print('没有找到 node ,请到配置文件指定路径')

#每次打开文件都会触发这个事件
class ExecEventListener(sublime_plugin.EventListener):
    def on_load(self, view):
        # 设置所有缩进为2
        fn,ext = os.path.splitext(view.file_name())
        if ext == '.py':
            edit_view_settings = view.settings()
            edit_view_settings.set("tab_size", 4)
        else:
            edit_view_settings = view.settings()
            edit_view_settings.set("tab_size", 2)

# 如果安装了 sublimeREPL 插件
class OpenShellREPL(sublime_plugin.WindowCommand):
    def run(self,edit,mode=False):
        # 将屏幕分成上下两块，并且在新建的视图内嵌入shell
        self.window.run_command('set_layout', {"cells": [[0, 0, 1, 1], [0, 1, 1, 2]], "cols": [0.0,1.0], "rows": [0.0,0.7, 1.0]})

        # 调用 sublimeREPL 的命令，创建SHELL到视图
        self.window.run_command('run_existing_window_command', {"file": "config/Shell/Main.sublime-menu", "id": "repl_shell"})


# 设置语法
class TesttSetSyntax(sublime_plugin.TextCommand):
    def run(self,edit,syntax):
        syntaxDict = {
          "html":"Packages/HTML/HTML.sublime-syntax",
          "vue":"Packages/Vue Syntax Highlight/Vue Component.sublime-syntax",
          "js":"Packages/JavaScriptNext - ES6 Syntax/JavaScriptNext.tmLanguage"
        }
        self.view.set_syntax_file(syntaxDict[syntax])

# 开启 sbulime 的记录模式(Debug)
class TesttDebug(sublime_plugin.TextCommand):
    def run(self,edit):
        global DEBUG_FALG
        if DEBUG_FALG:
            DEBUG_FALG = False
        else:
            DEBUG_FALG = True
        sublime.log_commands(DEBUG_FALG)
        sublime.log_input(DEBUG_FALG)


class TsttShowCurrtScope(sublime_plugin.TextCommand):
    def run(self,edit):
        print('当前Scope是:'+ str(DEBUG_FALG))


# 默认快捷键 alt+u
# 更新本地的 /testt/channel_v3/channel_v3.json 到packcontrol内
class TesttUpdateChannel(sublime_plugin.TextCommand):
    def run(self,edit):
        # 验证 channel_v3 文件是否存在，不存在则不执行
        channelFile = os.path.join(sublime.packages_path(),'testt','channel_v3','channel_v3.json')
        if not (os.path.exists(channelFile)):
            return print('没有发现channelFile文件,请将其存放到:'+channelFile)
        newChannel = {}
        newChannel['channels'] = [channelFile]

        # 验证 user是否存在
        userDir = os.path.join(sublime.packages_path(),'User')
        if not (os.path.exists(userDir)):
            return False

        # 判断Package Control.sublime-settings 是否存在
        pcfile = os.path.join(userDir,'Package Control.sublime-settings')
        tar = False

        # 添加json5模块
        sys.path.append(os.path.dirname(__file__))
        import json5

        # 判断配置文件是否存在
        if not (os.path.exists(pcfile)):
            print('没有发现 Package Control.sublime-settings 文件')
            newFile = open(
                pcfile,
                "w",
                encoding='utf8'
                )
            newFile.close()

        try:
            # 读取配置文件
            with open(pcfile,'r',encoding='utf8') as f:
                tar = json5.load(f)
                # 验证是否需要添加
                tmp = tar.get('channels',False)
                if tmp and channelFile in tmp:
                    f.close()
                    return print('channel已存在，不需要更新')

                # 更新配置文件
                tar.update(newChannel)
                f.close()

            # 写入新文件
            pcfile = os.path.join(userDir,'Package Control.sublime-settings')
            with open(pcfile,'w',encoding='utf8') as f2:
                res = json.dump(tar,f2,sort_keys=True,indent='  ',ensure_ascii=False)
                f2.close()

            print('成功更新channels')
        except Exception:
            print('发生错误了'+Exception)

# 打开当前文件的文件夹
class TesttOpenCurrtFolderCommand(sublime_plugin.TextCommand):
    # 打开当前文件所在的文件夹
    # 使用os模块打开系统的某个文件夹，但无法置前

    def run(self, edit):
        (fdir, fname) = os.path.split(self.view.file_name())
        currtWindow = sublime.active_window()  # 获取当前激活的窗口
        currtWindow.run_command("open_dir", {"dir": fdir})


# 打开指定的文件夹
class TesttOpenFolderCommand(sublime_plugin.WindowCommand):
    # 打开设置好的文件夹里
    # 调用sublime内置的命令打开文件夹，默认之前

    def run(self, *edit, **args):
        if isinstance(args, dict):
            if 'path' in args:
                print(args['path'])
                if args['path'].startswith('$'):
                    self.window.run_command("open_dir", {"dir": args['path']})
                    return 1
                elif os.path.exists(args['path']):
                    if os.path.isdir(args['path']):
                        self.window.run_command(
                            "open_dir", {"dir": args['path']})
                        return 1
        sublime.message_dialog('\n请检查： “%s”， 是否合法的路径。\n' % args['path'])
        return 0


##
## @brief      格式化 vue、stylus、pug文件
##
class TesttBeautifyCommand(sublime_plugin.TextCommand):
    def run(self, edit, mode=''):
        import testtlib

        # 判断当前语法是否合法
        NODE_SCRIPT_PATH = os.path.join(sublime.packages_path(),"testt","Plugins","index.js")
        if mode:
          SYNTAX = mode
        else:
          SYNTAX = testtlib.check_stynax(self.view.file_name())

        support_mode = ['pug','vue','stylus','html','js','html2pug']
        CONFIG = {}

        # 根据，获取对应用户配置
        if SYNTAX in support_mode:
            if TESTT_SETTINGS.has(SYNTAX+'_options'):
                CONFIG = TESTT_SETTINGS.get(SYNTAX+'_options')
            CONFIG = json.dumps(CONFIG)
            print('当前格式化文件',SYNTAX,CONFIG)
        else:
            return print('不支持的格式',SYNTAX)

        # 要处理的 regionList
        regionList = []
        strList = []
        replaceEegion = []
        vue = [] # vue的容器 内部记录范围和格式

        # 格式化整个文件
        if not self.has_selection():
            region = ""
            if SYNTAX == 'vue':
                # 获取 html 模块的范围
                templateEegion = self.get_vue_template_region()
                if templateEegion:
                    vue.append({
                      "syntax":'pug',
                      "start":templateEegion[0],
                      "end":templateEegion[1]
                      })

                # 获取 style 的范围
                styleEegion = self.get_vue_style_region()
                if styleEegion:
                    vue.append({
                      "syntax":'stylus',
                      "start":styleEegion[0],
                      "end":styleEegion[1]
                      })

                # 获取 js 的范围
                #jsEegion = self.get_vue_js_region()
            else:
              regionList.append(sublime.Region(0, self.view.size()))

        else:
            # 格式化已选的区域
            if SYNTAX == 'vue': return print('暂不支持vue局部选区')
            for region in self.view.sel():
                if region.empty():
                  continue

                # 将选区内容转换为字符串
                regionList.append(region)
                # strList.append(self.view.substr(region))

        # 将每个视数据传给 node 进行格式化
        if SYNTAX == 'vue':
            # 先对vue 数组根据范围先后进行倒排序
            vue.sort(key=lambda x:x['start'], reverse=True)

            for each in vue:
                CONFIG = {}
                if TESTT_SETTINGS.has(each['syntax']+'_options'):
                    CONFIG = TESTT_SETTINGS.get(each['syntax']+'_options')
                CONFIG = json.dumps(CONFIG)
                print(each['start'], each['end'],each['syntax'])

                eachRegion = sublime.Region(each['start'], each['end'])

                strBuffer = self.view.substr(eachRegion)

                combed = testtlib.node(strBuffer,['node', NODE_SCRIPT_PATH,CONFIG,each['syntax']])
                if combed:
                    #替换选区的内容为处理后的数据
                    self.view.replace(edit, eachRegion, combed)

        else:
            for eachRegion in regionList:
                strBuffer = self.view.substr(eachRegion)
                combed = testtlib.node(strBuffer,['node', NODE_SCRIPT_PATH,CONFIG,SYNTAX])
                if combed:
                    #替换选区的内容为处理后的数据
                    self.view.replace(edit, eachRegion, combed)
                return


    # 判断已选择的缓冲区是否合法(有时候时多个,且不连续,那是不合法的)
    def has_selection(self):
        for sel in self.view.sel():
            start, end = sel
            if start != end:
                return True
        return False

    ##
    ## @brief      返回vue 文件内 <style> 的内容
    ##
    def get_vue_style_region(self,syntax='stylus'):
        if syntax == 'stylus' or syntax == 'styl':
            syntax = r'sty[l|lus]'

        start = self.view.find(r'<style.*lang=[\'|\"]' + syntax + r'.*[\'|\"].*?>',0)
        end = self.view.find(r'<\/style>$',start.b-1)
        #print(start,end)
        if start and end:
            if start.b == end.a: return False #标签内没有内容
            # return sublime.Region(start.b+1, end.a-1)
            return (start.b+1, end.a-1)
        return False

    ##
    ## @brief      返回vue内 lang=pug的内容
    ##
    def get_vue_template_region(self,syntax='pug'):
        start = self.view.find(r'<template.*lang=[\'|\"]' + syntax + r'[\'|\"].*?>',0)
        end = self.view.find(r'<\/template>$',start.b-1)

        print('vue-pug',start,end)

        if start and end:
            if start.b == end.a: return False #标签内没有内容
            # return sublime.Region(start.b+1, end.a-1)
            return (start.b+1, end.a-1)
        return False

##
## @brief      根据 testtfileheader文件夹下 header的文件 创建头部信息
##
class TesttAddFileHeader(sublime_plugin.TextCommand):
    def run(self,edit):
        print('执行 TesttAddFileHeader')
        tmplfile = ''

        #获取配置
        settings = self.get_settings()
        if not settings:
            return print('没法找到settings')

        # 获取对应的tmpl关系
        template = settings['template']
        info = settings['headerInfo']

        # 文件字符串少于2的话,当作是一个新文件,添加创建时间
        if self.view.size() <= 1:
            info = self.on_add_file(info)
        info = self.on_modi_file(info)
        res = ''

        # 判断当前格式是否有对应的模版文件
        tmplfile = self.get_tmpl(template)

        # 验证 tmpl文件
        if not os.path.exists(tmplfile):
            return print('没有发现tmpl文件'+tmplfile)

        # 读取tmpl文件
        res = ''
        with open(tmplfile,'r',encoding='utf8') as tmpl:
            for each in tmpl.readlines():
                for key,val in info.items():
                    each = each.replace('{{'+key+'}}',val)
                res+=each

        # 插入文件头
        if res:
            self.view.insert(edit,0,res)

    def get_tmpl(self,template):
        TMPL_HEADER_PATH = os.path.join(TESTT_PATH,"fileheader","header")
        (name,ext) = os.path.splitext(self.view.file_name())
        ext = ext.replace('.','')
        if ext in template.keys():
            return os.path.join(TMPL_HEADER_PATH,template[ext])+'.tmpl'

    def get_settings(self):
        settings = self.view.settings().get('testt')
        if settings is None:
            print('settings is None')
            settings = sublime.load_settings('testt.sublime-settings')

        if settings.has('file_header_options'):
            tmp = settings.get('file_header_options')
            return tmp

        return settings

    def on_add_file(self,info):
        info['create_time'] = self.get_now(info['create_time'])
        return info

    def on_modi_file(self,info):
        # datetime.datetime.now()
        info['last_modified_time'] = self.get_now(info['last_modified_time'])
        info['file_path'] = os.path.dirname(self.view.file_name())

        fn = os.path.basename(self.view.file_name())
        if fn.startswith('index'):
          # 如果文件是 index.xxxx, 就把文件夹名作为文件名
          info['file_name'] = os.path.basename(info['file_path'])
        else:
          info['file_name'] = fn

        return info

    def get_now(self,fmat):
        try:
            if not fmat:
                fmat = r'%Y-%m-%d %H:%M:%S'
            return datetime.datetime.now().__format__(fmat)
        except Exception as e:
            return datetime.datetime.now().__format__(r'%Y-%m-%d %H:%M:%S')